/* Copyright 2012 Oliver Katz

    This file is part of Theta.

    Theta is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "data.h"

t_data_meta t_data_meta_init()
{
	t_data_meta tmp;
	tmp.type = N0;
	tmp.data = NULL;
	return tmp;
}

t_data_meta t_data_meta_init_with_type(t_data_meta_type t)
{
	t_data_meta tmp = t_data_meta_init();
	tmp.type = t;
	return tmp;
}

t_data_meta t_data_meta_init_with_type_value(t_data_meta_type t, long v)
{
	t_data_meta tmp = t_data_meta_init_with_type(t);
	
	if (t == N0)
	{
		tmp.data = NULL;
	}
	else if (t == S8)
	{
		tmp.data = malloc(sizeof(char));
		memcpy(tmp.data, &v, sizeof(char));
	}
	else if (t == S16)
	{
		tmp.data = malloc(sizeof(short));
		memcpy(tmp.data, &v, sizeof(short));
	}
	else if (t == S32)
	{
		tmp.data = malloc(sizeof(int));
		memcpy(tmp.data, &v, sizeof(int));
	}
	else if (t == S64)
	{
		tmp.data = malloc(sizeof(long));
		memcpy(tmp.data, &v, sizeof(long));
	}
	else if (t == H8)
	{
		tmp.data = (void *)(int)(char)v;
	}
	else if (t == H16)
	{
		tmp.data = (void *)(int)(short)v;
	}
	else if (t == H32)
	{
		tmp.data = (void *)(int)v;
	}
	else if (t == SC)
	{
		tmp.data = NULL;
	}

	return tmp;
}

t_data_meta t_data_meta_init_compound(int n, t_data_meta *t)
{
	t_data_meta tmp = t_data_meta_init_with_type(SC);
	tmp.data = malloc(sizeof(int)+sizeof(t_data_meta)*n);
	*(int *)tmp.data = n;
	memcpy(tmp.data+sizeof(int), t, sizeof(t_data_meta)*n);
	return tmp;
}

t_data_meta_type t_data_meta_get_type(t_data_meta *m)
{
	return m->type;
}

int t_data_meta_compound_get_size(t_data_meta *m)
{
	return *(int *)m->data;
}

t_data_meta t_data_meta_compound_get(t_data_meta *m, int n)
{
	return *(t_data_meta *)(m->data+sizeof(int)+sizeof(t_data_meta)*n);
}

void *t_data_meta_get_value(t_data_meta *m)
{
	return m->data;
}

void t_data_meta_del(t_data_meta *m)
{
	if (m->data != NULL && ((m->type > N0 && m->type < H8) || m->type == SC))
		free(m->data);
}

t_data_meta_page t_data_meta_page_init()
{
	t_data_meta_page tmp;
	tmp.root = (t_data_meta *)malloc(sizeof(t_data_meta)*T_DATA_META_PAGE_INITSIZE);
	memset(tmp.root, 0, sizeof(t_data_meta)*T_DATA_META_PAGE_INITSIZE);
	tmp.head = tmp.root;
	tmp.size = T_DATA_META_PAGE_INITSIZE;
	return tmp;
}

int t_data_meta_page_put_dirty(t_data_meta_page *p, t_data_meta m)
{
	if ((p->head-p->root)-1 >= p->size)
	{
		int s = p->size + T_DATA_META_PAGE_INCSIZE;
		t_data_meta *tmp = (t_data_meta *)malloc(sizeof(t_data_meta)*s);
		memset(tmp, 0, sizeof(t_data_meta)*s);
		memcpy(tmp, p->root, sizeof(t_data_meta)*p->size);
		p->root = tmp;
		p->size = s;
	}

	*(p->head) = m;
	p->head++;

	return (p->head-p->root)-1;
}

int t_data_meta_page_put_clean(t_data_meta_page *p, t_data_meta m)
{
	for (int i = 0; i < p->size; i++)
	{
		if (p->root[i].type == N0)
		{
			p->root[i] = m;
			return i;
		}
	}

	return t_data_meta_page_put_dirty(p, m);
}

t_data_meta t_data_meta_page_get(t_data_meta_page *p, int addr)
{
	return p->root[addr];
}

void t_data_page_rem(t_data_meta_page *p, int addr)
{
	p->root[addr].type = N0;
	p->root[addr].data = NULL;
}

void t_data_meta_page_del(t_data_meta_page *p)
{
	free(p->root);
}